package com.zim.ta_service.support;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.Nullable;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.net.*;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Set;
import java.util.function.Predicate;

import static com.google.common.collect.Sets.newHashSet;

/**
 * author: alabimofa <br>
 * date: 2021/11/3 10:25 <br>
 */
@Slf4j
public class WebUtil {

	private static final String[] IP_HEADER_NAMES = new String[]{
		"x-forwarded-for",
		"Proxy-Client-IP",
		"WL-Proxy-Client-IP",
		"HTTP_CLIENT_IP",
		"HTTP_X_FORWARDED_FOR"
	};

	/**
	 * 获取 HttpServletRequest
	 * @return {HttpServletRequest}
	 */
	public static HttpServletRequest getRequest() {
		RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
		return (requestAttributes == null) ? null : ((ServletRequestAttributes) requestAttributes).getRequest();
	}

	/**
	 * 获取ip
	 * @param request HttpServletRequest
	 * @return {String}
	 */
	@Nullable
	public static String getIP(@Nullable HttpServletRequest request) {
		if (request == null) {
			return StringPool.EMPTY;
		}
		String ip = null;
		for (String ipHeader : IP_HEADER_NAMES) {
			ip = request.getHeader(ipHeader);
			if (!IP_PREDICATE.test(ip)) {
				break;
			}
		}
		if (IP_PREDICATE.test(ip)) {
			ip = request.getRemoteAddr();
		}
		return StrUtil.isBlank(ip) ? null : StrUtil.splitTrim(ip, StrUtil.COMMA).get(0);
	}

	private static final Predicate<String> IP_PREDICATE = (ip) -> StrUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip);


	/**
	 * Given an address resolve it to as many unique addresses or hostnames as can be found.
	 *
	 * @param address the address to resolve.
	 * @return the addresses and hostnames that were resolved from {@code address}.
	 */
	public static Set<String> getHostNames(String address) {
		return getHostNames(address, true);
	}

	/**
	 * Given an address resolve it to as many unique addresses or hostnames as can be found.
	 *
	 * @param address         the address to resolve.
	 * @param includeLoopback if {@code true} loopback addresses will be included in the returned set.
	 * @return the addresses and hostnames that were resolved from {@code address}.
	 */
	public static Set<String> getHostNames(String address, boolean includeLoopback) {
		Set<String> hostNames = newHashSet();

		try {
			InetAddress inetAddress = InetAddress.getByName(address);

			if (inetAddress.isAnyLocalAddress()) {
				try {
					Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();

					for (NetworkInterface ni : Collections.list(nis)) {
						Collections.list(ni.getInetAddresses()).forEach(ia -> {
							if (ia instanceof Inet4Address) {
								boolean loopback = ia.isLoopbackAddress();

								if (!loopback || includeLoopback) {
									hostNames.add(ia.getHostName());
									hostNames.add(ia.getHostAddress());
									hostNames.add(ia.getCanonicalHostName());
								}
							}
						});
					}
				} catch (SocketException e) {
					log.warn("Failed to NetworkInterfaces for bind address: {}", address, e);
				}
			} else {
				boolean loopback = inetAddress.isLoopbackAddress();

				if (!loopback || includeLoopback) {
					hostNames.add(inetAddress.getHostName());
					hostNames.add(inetAddress.getHostAddress());
					hostNames.add(inetAddress.getCanonicalHostName());
				}
			}
		} catch (UnknownHostException e) {
			log.warn("Failed to get InetAddress for bind address: {}", address, e);
		}
		return hostNames;
	}
}
